package retrofit.boot.annotation;

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.*;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Created by wulg on 2019/11/12
 * description:
 */
public abstract class RetrofitClientRegistrySupport implements ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

    private static Set<String> aliasSet = new CopyOnWriteArraySet<>();

    private ResourceLoader resourceLoader;

    private ClassLoader classLoader;

    private Environment environment;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    protected final void registerRetrofitClients(BeanDefinitionRegistry registry, RetrofitClientWrapper wrapper){
        //注册endpoint
        String endpointAlias = AliasHelper.getEndpointAlias(wrapper.getName());
        if(!aliasSet.contains(endpointAlias)){
            aliasSet.add(endpointAlias);
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RetrofitEndpointFactoryBean.class);
            beanDefinitionBuilder.addPropertyValue("name", wrapper.getName());
            beanDefinitionBuilder.addPropertyValue("url", wrapper.getUrl());
            beanDefinitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
            AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
            beanDefinition.setPrimary(true);
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, endpointAlias);
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        }
        //注册client
        String clientAlias = AliasHelper.getClientAlias(wrapper.getName(),wrapper.getClassName());
        if(!aliasSet.contains(clientAlias)){
            aliasSet.add(clientAlias);
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RetrofitClientFactoryBean.class);
            beanDefinitionBuilder.addPropertyValue("name", wrapper.getName());
            beanDefinitionBuilder.addPropertyValue("type", wrapper.getClassName());
            beanDefinitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
            beanDefinition.setPrimary(wrapper.isPrimary());
            beanDefinition.setDependsOn(endpointAlias);
            String qualifier = wrapper.getQualifier();
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, wrapper.getClassName(),new String[] { StringUtils.hasText(qualifier)? qualifier : clientAlias });
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        }
    }

    protected final ClassPathScanningCandidateComponentProvider getScanner() {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                if (!beanDefinition.getMetadata().isIndependent()) {
                    return false;
                }
                if (beanDefinition.getMetadata().isInterface()
                        && beanDefinition.getMetadata().getInterfaceNames().length == 1
                        && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
                    try {
                        Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(),classLoader);
                        return !target.isAnnotation();
                    }
                    catch (Exception ex) {
                        this.logger.error("Could not load target class: "+ beanDefinition.getMetadata().getClassName(), ex);
                    }
                }
                return true;
            }
        };
        scanner.setResourceLoader(this.resourceLoader);
        return scanner;
    }

    protected final  String resolve(String value) {
        return StringUtils.hasText(value) ? this.environment.resolvePlaceholders(value) : value;
    }
}
